package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"
	"text/template"

	"github.com/xuri/excelize/v2"
	"gopkg.in/gomail.v2"
)

type Config struct {
	Excel struct {
		FilePath    string `json:"file_path"`
		SheetName   string `json:"sheet_name"`
		EmailColumn string `json:"email_column"`
		NameColumn  string `json:"name_column"`
	} `json:"excel"`
	SMTP struct {
		Host        string `json:"host"`
		Port        string `json:"port"`
		Username    string `json:"username"`
		Password    string `json:"password"`
		SenderEmail string `json:"sender_email"`
	} `json:"smtp"`
	Email struct {
		Subject  string `json:"subject"`
		Template string `json:"template"`
	} `json:"email"`
}

// ✅ 有序工资明细
type KeyValue struct {
	Key   string
	Value string
}

type EmployeeSalary struct {
	Name    string
	Email   string
	Details []KeyValue // 按 Excel 顺序保存工资条明细
}

type EmailData struct {
	Name    string
	Details []KeyValue
}

var (
	config      Config
	subjectTmpl *template.Template
	bodyTmpl    *template.Template
)

func main() {
	if err := loadConfig("config.json"); err != nil {
		log.Fatalf("❌ 配置加载失败: %v", err)
		waitForExit()
	}

	if err := prepareTemplates(); err != nil {
		log.Fatalf("❌ 模板解析失败: %v", err)
		waitForExit()
	}

	employees, err := readExcelFile(config.Excel.FilePath)
	if err != nil {
		log.Printf("❌ Excel读取失败: %v", err)
		waitForExit()
		return
	}

	for _, emp := range employees {
		if err := sendSalaryEmail(emp); err != nil {
			log.Printf("❌ [%s %s] 邮件发送失败: %v", emp.Name, emp.Email, err)
		} else {
			log.Printf("✅ [%s %s] 邮件发送成功", emp.Name, emp.Email)
		}
	}

	log.Println("🎉 所有工资条发送完成!")

	waitForExit()
}

func loadConfig(filename string) error {
	data, err := os.ReadFile(filename)
	if err != nil {
		return err
	}
	return json.Unmarshal(data, &config)
}

func prepareTemplates() error {
	var err error
	subjectTmpl, err = template.New("subject").Parse(config.Email.Subject)
	if err != nil {
		return fmt.Errorf("主题模板解析失败: %w", err)
	}
	bodyTmpl, err = template.New("body").Parse(config.Email.Template)
	if err != nil {
		return fmt.Errorf("正文模板解析失败: %w", err)
	}
	return nil
}

func readExcelFile(filePath string) ([]EmployeeSalary, error) {
	f, err := excelize.OpenFile(filePath)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	rows, err := f.GetRows(config.Excel.SheetName)
	if err != nil {
		return nil, err
	}
	if len(rows) < 2 {
		return nil, fmt.Errorf("没有数据")
	}

	headers := rows[0]
	colMap := make(map[string]int)
	for i, h := range headers {
		colMap[strings.TrimSpace(h)] = i
	}

	emailIdx, ok := colMap[config.Excel.EmailColumn]
	if !ok {
		return nil, fmt.Errorf("找不到邮箱列: %s", config.Excel.EmailColumn)
	}
	nameIdx, ok := colMap[config.Excel.NameColumn]
	if !ok {
		return nil, fmt.Errorf("找不到姓名列: %s", config.Excel.NameColumn)
	}

	// ✅ 保存工资明细列的顺序
	var detailCols []int
	for i := range headers {
		if i != emailIdx && i != nameIdx && strings.TrimSpace(headers[i]) != "" {
			detailCols = append(detailCols, i)
		}
	}

	var employees []EmployeeSalary
	for i, row := range rows[1:] {
		if len(row) <= emailIdx || len(row) <= nameIdx {
			log.Printf("⚠️ 第%d行数据不完整，跳过", i+2)
			continue
		}

		email := strings.TrimSpace(row[emailIdx])
		if !strings.Contains(email, "@") {
			log.Printf("⚠️ 第%d行邮箱无效: %s", i+2, email)
			continue
		}

		emp := EmployeeSalary{
			Name:    strings.TrimSpace(row[nameIdx]),
			Email:   email,
			Details: []KeyValue{},
		}

		// ✅ 按原顺序存储工资条内容
		for _, colIdx := range detailCols {
			val := ""
			if colIdx < len(row) {
				val = strings.TrimSpace(row[colIdx])
			}
			emp.Details = append(emp.Details, KeyValue{
				Key:   headers[colIdx],
				Value: val,
			})
		}
		employees = append(employees, emp)
	}
	return employees, nil
}

func sendSalaryEmail(emp EmployeeSalary) error {
	data := EmailData{Name: emp.Name, Details: emp.Details}

	var subjectBuf, bodyBuf bytes.Buffer
	if err := subjectTmpl.Execute(&subjectBuf, data); err != nil {
		return err
	}
	if err := bodyTmpl.Execute(&bodyBuf, data); err != nil {
		return err
	}

	m := gomail.NewMessage()
	m.SetHeader("From", config.SMTP.SenderEmail)
	m.SetHeader("To", emp.Email)
	m.SetHeader("Subject", subjectBuf.String())
	m.SetBody("text/plain", bodyBuf.String())

	d := gomail.NewDialer(config.SMTP.Host, mustAtoi(config.SMTP.Port), config.SMTP.Username, config.SMTP.Password)
	d.SSL = true // ✅ 适配 465 SSL

	return d.DialAndSend(m)
}

func mustAtoi(s string) int {
	var n int
	fmt.Sscanf(s, "%d", &n)
	return n
}

func waitForExit() {
	fmt.Println("\n程序执行完成，按 Enter 键退出...")
	fmt.Scanln()
}
